using System;
using comm;
using Microsoft.VisualStudio.Debugger.Interop;
using Red.Scripts.Debugger.Defines;

namespace Red.Scripts.Debugger.Remote.Debugger
{
    // Represents a logical stack frame on the thread stack. 
    // Also implements the IDebugExpressionContext interface, which allows expression evaluation and watch windows.
    class StackFrame : IDebugStackFrame2//, IDebugExpressionContext2
    {
        public Thread Parent { get; private set; }
        public ScriptStackFrame Frame { get; private set; }

        public StackFrame( Thread parent, ScriptStackFrame frame )
        {
            Parent = parent;
            Frame = frame;
        }

        public FRAMEINFO CreateFrameInfo( enum_FRAMEINFO_FLAGS dwFieldSpec )
        {
            FRAMEINFO frame = new FRAMEINFO();

            if ( Utility.HasFlag( dwFieldSpec, enum_FRAMEINFO_FLAGS.FIF_FUNCNAME ) )
            {
                frame.m_bstrFuncName = Frame.func;

                frame.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FUNCNAME;
            }

            if( Utility.HasFlag( dwFieldSpec, enum_FRAMEINFO_FLAGS.FIF_LANGUAGE ) )
            {
                frame.m_bstrLanguage = Language.Name;
                frame.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_LANGUAGE;
            }

            if ( Utility.HasFlag( dwFieldSpec, enum_FRAMEINFO_FLAGS.FIF_FRAME ) )
            {
                frame.m_pFrame = this;
                frame.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FRAME;
            }

            if ( Utility.HasFlag( dwFieldSpec, enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO ) )
            {
                frame.m_fHasDebugInfo = 1;
                frame.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO;
            }

            if ( Utility.HasFlag( dwFieldSpec, enum_FRAMEINFO_FLAGS.FIF_STALECODE ) )
            {
                frame.m_fStaleCode = 0;
                frame.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STALECODE;
            }

            return frame;
        }

        #region IDebugStackFrame2 Members

        int IDebugStackFrame2.EnumProperties( enum_DEBUGPROP_INFO_FLAGS dwFields, uint nRadix, ref Guid guidFilter, uint dwTimeout, out uint pcelt, out IEnumDebugPropertyInfo2 ppEnum )
        {
            DEBUG_PROPERTY_INFO[] properties = Property.Enumerate( Frame.locals, Parent.Parent.Channel, dwFields, Parent.Data.id, Frame.id );

            pcelt = (uint)Frame.locals.Count;
            ppEnum = new Defines.Enumerators.Property( properties );

            return Constants.S_OK;
        }

        // Gets the code context for this stack frame. The code context represents the current instruction pointer in this stack frame.
        int IDebugStackFrame2.GetCodeContext( out IDebugCodeContext2 memoryAddress )
        {
            memoryAddress = null;

            return Constants.E_NOTIMPL;
        }

        // Gets a description of the properties of a stack frame.
        // Calling the IDebugProperty2::EnumChildren method with appropriate filters can retrieve the local variables, method parameters, registers, and "this" 
        // pointer associated with the stack frame. The debugger calls EnumProperties to obtain these values in the sample.
        int IDebugStackFrame2.GetDebugProperty( out IDebugProperty2 property )
        {
            property = null;

            return Constants.E_NOTIMPL;
        }

        // Gets the document context for this stack frame. The debugger will call this when the current stack frame is changed
        // and will use it to open the correct source document for this stack frame.
        int IDebugStackFrame2.GetDocumentContext( out IDebugDocumentContext2 docContext )
        {
            docContext = new DocumentContext( Frame );

            return Constants.S_OK;
        }

        // Gets an evaluation context for expression evaluation within the current context of a stack frame and thread.
        // Generally, an expression evaluation context can be thought of as a scope for performing expression evaluation. 
        // Call the IDebugExpressionContext2::ParseText method to parse an expression and then call the resulting IDebugExpression2::EvaluateSync 
        // or IDebugExpression2::EvaluateAsync methods to evaluate the parsed expression.
        int IDebugStackFrame2.GetExpressionContext( out IDebugExpressionContext2 ppExprCxt )
        {
            ppExprCxt = new ExpressionEvaluation.StackContext( this );

            return Constants.S_OK;
        }

        // Gets a description of the stack frame.
        int IDebugStackFrame2.GetInfo( enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, FRAMEINFO[] pFrameInfo )
        {
            pFrameInfo[ 0 ] = CreateFrameInfo( dwFieldSpec );

            return Constants.S_OK;
        }

        // Gets the language associated with this stack frame. 
        // In this sample, all the supported stack frames are C++
        int IDebugStackFrame2.GetLanguageInfo( ref string pbstrLanguage, ref Guid pguidLanguage )
        {
            pbstrLanguage = Language.Name;
            pguidLanguage = Language.Guid;

            return Constants.S_OK;
        }

        // Gets the name of the stack frame.
        // The name of a stack frame is typically the name of the method being executed.
        int IDebugStackFrame2.GetName( out string name )
        {
            name = Frame.func;

            return Constants.S_OK;
        }

        // Gets a machine-dependent representation of the range of physical addresses associated with a stack frame.
        int IDebugStackFrame2.GetPhysicalStackRange( out ulong addrMin, out ulong addrMax )
        {
            addrMin = 0;
            addrMax = 0;

            return Constants.E_NOTIMPL;
        }

        // Gets the thread associated with a stack frame.
        int IDebugStackFrame2.GetThread( out IDebugThread2 thread )
        {
            thread = Parent;

            return Constants.S_OK;
        }

        #endregion

        #region IDebugExpressionContext2 Members
        /*
        // Retrieves the name of the evaluation context. 
        // The name is the description of this evaluation context. It is typically something that can be parsed by an expression evaluator 
        // that refers to this exact evaluation context. For example, in C++ the name is as follows: 
        // "{ function-name, source-file-name, module-file-name }"
        int IDebugExpressionContext2.GetName( out string pbstrName )
        {
            throw new NotImplementedException();
        }

        // Parses a text-based expression for evaluation.
        // The engine sample only supports locals and parameters so the only task here is to check the names in those collections.
        int IDebugExpressionContext2.ParseText( string pszCode,
                                                enum_PARSEFLAGS dwFlags,
                                                uint nRadix,
                                                out IDebugExpression2 ppExpr,
                                                out string pbstrError,
                                                out uint pichError )
        {
            throw new NotImplementedException();
        }
        //*/
        #endregion
    }
}
